#pragma once

#include "Common.h"
#include "Vector2.h"
#include "Vector3.h"
#include "Color4.h"
#include "BoundingBox.h"
#include "Texture2D.h"
#include "VertexBuffer.h"
#include "IndexBuffer.h"
#include "Ray.h"
#include <bitset>

#define BSP_MAGIC		0x50534249	// IBSP
#define BSP_VERSION		0x0000002E	// 46
#define	BSP_EPSILON		0.03125f

#define TYPE_RAY		0				// This is the type for tracing a RAY
#define TYPE_SPHERE		1				// This is the type for tracing a SPHERE

struct BspHeader
{
public:
	int Magic;
	int Version;
};

struct BspLump
{
public:
	int Offset;
	int Length;
};

struct BspVertex
{
public:
	Vector3f		Position;
	Vector2f		TextureCoordinate;
	Vector2f		LightmapCoordinate;
	Vector3f		Normal;
	unsigned char	Color[4];
};

struct BspFace
{
public:
	int			TextureIndex;
	int			Effect;
	int			Type;
	int			StartVertexIndex;
	int			VertexCount;
	int			StartIndex;
	int			IndexCount;
	int			LightmapID;
	int			LightmapCorner[2];
	int			LightmapSize[2];
	Vector3f	LightmapPosition;
	Vector3f	LightmapVectors[2];
	Vector3f	Normal;
	int			Size[2];
};

struct BspTexture
{
public:
	char	Name[64];
	int		Flags;
	int		Type;
};

struct BspLightmap
{
public:
	unsigned char Bytes[128][128][3];
};

struct BspNode
{
public:
	int	PlaneIndex;
	int Children[2];
	int	BoundingBox[6];
};

struct BspLeaf
{
public:
	int			Cluster;
	int			Area;
	int			BoundingBox[6];
	int			FaceIndex;
	int			FaceCount;
	int			BrushIndex;
	int			BrushCount;
};

struct BspPlane
{
public:
	Vector3f Normal;
	float	Distance;
};

struct BspVisiblity
{
public:
	int ClusterCount;
	int BytesPerCluster;
	unsigned char* Bits;
};

struct BspBrush
{
public:
	int	BrushSideIndex;
	int	BrushSideCount;
	int TextureIndex;
};

struct BspBrushSide
{
public:
	int	PlaneIndex;
	int	TextureIndex;
};

struct BspModel
{
public:
	int			BoundingBox[6];
	int			FaceIndex;
	int			FaceCount;
	int			BrushIndex;
	int			BrushCount;
};

struct BspShader
{
public:
	char	Name[64];
	int		BrushIndex;
	int		Unknown;
};

struct BspLightVolume
{
public:
	unsigned char	Ambient[3];
	unsigned char	Directional[3];
	unsigned char	Direction[2];
};

enum BspLumps
{
	BSPLUMPS_ENTITIES = 0,
    BSPLUMPS_TEXTURES,
    BSPLUMPS_PLANES,
    BSPLUMPS_NODES,
    BSPLUMPS_LEAVES,
    BSPLUMPS_LEAFFACES,
    BSPLUMPS_LEAFBRUSHES,
    BSPLUMPS_MODELS,
    BSPLUMPS_BRUSHES,
    BSPLUMPS_BRUSHSIDES,
    BSPLUMPS_VERTICES,
    BSPLUMPS_INDICES,
    BSPLUMPS_SHADERS,
    BSPLUMPS_FACES,
    BSPLUMPS_LIGHTMAPS,
    BSPLUMPS_LIGHTVOLUMES,
    BSPLUMPS_VISDATA,
    BSPLUMPS_COUNT
};

class Bsp
{
public:
	Bsp();
public:
	~Bsp();
public:
	void Load(std::string path);
	void Unload();
	void Draw();
private:
	const int				FindLeaf(const Vector3f& position) const;
	void					DrawFace(BspFace* face);
	bool					IsClusterVisible(int current, int test);
	void					CreateLightmapTexture(unsigned int& id, unsigned char* bytes, int width, int height);
	void					ChangeGamma(unsigned char* bytes, int size, float factor);
private:
	int						m_nVertexCount;
	int 					m_nFaceCount;
	int 					m_nIndexCount;
	int 					m_nTextureCount;
	int 					m_nLightmapCount;
	int 					m_nNodeCount;
	int						m_nLeafCount;
	int						m_nPlaneCount;
	int						m_nBrushCount;
	int 					m_nBrushSideCount;
	int						m_nLeafBrushCount;
	int						m_nLeafFaceCount;
	BspFace*				m_pFaces;
	BspTexture*				m_pTextures;
	BspNode*				m_pNodes;
	BspLeaf*				m_pLeaves;
	BspPlane*				m_pPlanes;
	BspBrush*				m_pBrushes;
	BspBrushSide*			m_pBrushSides;
	int*					m_pLeafBrushes;
	int*					m_pLeafFaces;
	BspVisiblity			m_oVisData;
	std::vector<Texture2D*>	m_pTexture2Ds;				// TODO: Consider name and type revision due to conflict.
	unsigned int*			m_pLightmapTextureIndices;	// TODO: Rename, ugly.
	std::bitset<4096>		m_nFacesDrawn;				// TODO: Not scalable.  Figure out how to do this with dynamic memory allocation.
	IndexBuffer*			m_pIndexBuffer;
	VertexBuffer*			m_pVertexBuffer;

	Texture2D*				m_pLastDiffuseTexture;
	int						m_nLastLightmapTexture;
public:
	Vector3f Trace(Vector3f vStart, Vector3f vEnd);
	Vector3f TraceRay(Vector3f vStart, Vector3f vEnd);
	Vector3f TraceSphere(Vector3f vStart, Vector3f vEnd, float radius);
	void CheckNode(int nodeIndex, float startRatio, float endRatio, Vector3f vStart, Vector3f vEnd);
	void CheckBrush(BspBrush *pBrush, Vector3f vStart, Vector3f vEnd);
	void TraceNode(int nodeIndex, float startFraction, float endFraction, Vector3f start, Vector3f end);
	void TraceBrush(BspBrush *brush, Vector3f inputStart, Vector3f inputEnd);
public:
	int	m_traceType;
	bool m_bCollided;
	float m_traceRadius;
	float m_traceRatio;
//public:
//	float					m_fOutputFraction;
//	Vector3f				m_vOutputEnd;
//	bool					m_bOutputStartsOut;
//	bool					m_bOutputAllSolid;
//public:
//	void					Trace(const Vector3f& begin, const Vector3f& end);
//private:
//	void					TraceNode(int nodeIndex, float startFraction, float endFraction, Vector3f begin, Vector3f end);
//	void					TraceBrush(const Vector3f& begin, const Vector3f& end, BspBrush* brush);
};